﻿using System;
using Microsoft.Xna.Framework.Net;

namespace BlockeyEssentials
{
    public static class Networking
    {

        public static void SendData()
        {
            if (_NetworkSession != null)
            {
                if (_NetworkSession.IsHost)
                {
                    if (CanHostSend)
                    {
                        if (_PacketWriter.Length > 0)
                        {
                            SendHostToClients();
                        }
                    }
                }
                else
                {
                    if (_PacketWriter.Length > 0)
                    {
                        SendClientToHost();
                    }
                }

                _SendCounter++;
                if (_SendCounter > _SkipFrames)
                    _SendCounter = 1;
            }
        }

        private static void SendHostToClients()
        {
            foreach (var oLocal in Networking.Session.LocalGamers)
            {
                if (oLocal.IsHost)
                {
                    oLocal.SendData(_PacketWriter, SendDataOptions.None);
                }
            }
        }

        private static void SendClientToHost()
        {
            foreach (var oLocal in Networking.Session.LocalGamers)
            {
                if (oLocal.IsHost == false)
                {
                    oLocal.SendData(_PacketWriter, SendDataOptions.None, _NetworkSession.Host);
                }
            }
        }

        #region Async Setup Methods

        public static void UpdateAsync()
        {
            if (_CreateResult != null && _CreateResult.IsCompleted)
            {
                EndCreateSession();
            }
            if (_FindResult != null && _FindResult.IsCompleted)
            {
                EndFindSessions();
            }
            if (_JoinResult != null && _JoinResult.IsCompleted)
            {
                EndJoinSession();
            }
        }

        public static void StartCreateSession(AsyncCallback oCallBack)
        {
            if (_JoinResult == null && _FindResult == null && _CreateResult == null)
            {
                if (_NetworkSession != null || _AvailableSessions != null)
                    Reset();

                _CreateCallBack = oCallBack;

                _CreateResult = NetworkSession.BeginCreate(
                    NetworkSessionType.SystemLink,
                    _MaxLocal,
                    _MaxTotal,
                    _PrivateSlots,
                    _NetProperties,
                    _CreateCallBack,
                    null);
            }
        }

        public static void EndCreateSession()
        {
            if (_NetworkSession != null)
            {
                Reset();
            }

            _NetworkSession = NetworkSession.EndCreate(_CreateResult);
            _CreateResult = null;

            _NetworkSession.AllowHostMigration = true;
            _NetworkSession.AllowJoinInProgress = true;
        }

        public static void StartJoinSession(AsyncCallback oCallBack)
        {
            if (_JoinResult == null && _FindResult == null && _CreateResult == null)
            {
                if (_NetworkSession != null)
                    Reset();

                _JoinCallBack = oCallBack;

                _JoinResult = NetworkSession.BeginJoin(
                    SelectedSession,
                    _JoinCallBack,
                    null);
            }
        }

        public static void EndJoinSession()
        {
            if (_NetworkSession != null)
            {
                Reset();
            }

            if (_JoinResult != null)
            {
                _NetworkSession = NetworkSession.EndJoin(_JoinResult);
                _JoinResult = null;
            }
        }

        public static void StartFindSessions(AsyncCallback oCallBack)
        {
            if (_JoinResult == null && _FindResult == null && _CreateResult == null)
            {
                if (_NetworkSession != null || _AvailableSessions != null)
                    Reset();

                _FindCallBack = oCallBack;

                _FindResult = NetworkSession.BeginFind(
                    NetworkSessionType.SystemLink,
                    _MaxLocal,
                    _NetProperties,
                    _FindCallBack,
                    null);
            }
        }

        public static void EndFindSessions()
        {
            if (_FindResult != null)
            {
                _AvailableSessions = NetworkSession.EndFind(_FindResult);
                _FindResult = null;
            }
        }

        #endregion

        #region Helper Methods

        public static void Reset()
        {
            if (_NetworkSession != null)
            {
                _NetworkSession.Dispose();
                _NetworkSession = null;
            }

            if (_AvailableSessions != null)
            {
                _AvailableSessions.Dispose();
                _AvailableSessions = null;
            }

            _SelectedAvailableSession = 0;
            _PacketReader = new PacketReader();
            _PacketWriter = new PacketWriter();

            _CreateResult = null;
            _CreateCallBack = null;
            _FindResult = null;
            _FindCallBack = null;
            _JoinResult = null;
            _JoinCallBack = null;

            _MaxLocal = 1;
            _MaxTotal = 8;
            _PrivateSlots = 0;
            _NetProperties = null;

            _SendCounter = 3;
            _SkipFrames = 2;
        }

        #endregion

        #region Properties

        public static bool CanHostSend
        {
            get { return _SendCounter >= _SkipFrames; }
        }

        public static bool IsRunning
        {
            get { return _NetworkSession != null; }
        }

        public static bool HasSearchResults
        {
            get { return _AvailableSessions != null && _AvailableSessions.Count > 0; }
        }

        public static NetworkSession Session
        {
            get { return _NetworkSession; }
        }

        public static AvailableNetworkSessionCollection AvailableSessions
        {
            get { return _AvailableSessions; }
        }

        public static AvailableNetworkSession SelectedSession
        {
            get
            {
                if (_AvailableSessions != null)
                {
                    if (_AvailableSessions.Count > 0)
                        return _AvailableSessions[_SelectedAvailableSession];
                }

                return null;
            }
        }

        public static PacketReader Reader
        {
            get { return _PacketReader; }
        }

        public static PacketWriter Writer
        {
            get { return _PacketWriter; }
        }

        public static int Counter
        {
            get { return _SendCounter; }
            set { _SendCounter = value; }
        }

        public static int FrameSkip
        {
            get { return _SkipFrames; }
            set { _SkipFrames = value; }
        }

        #endregion

        private static NetworkSession _NetworkSession;
        private static AvailableNetworkSessionCollection _AvailableSessions;
        private static int _SelectedAvailableSession;
        private static PacketReader _PacketReader;
        private static PacketWriter _PacketWriter;

        public static IAsyncResult _CreateResult;
        public static AsyncCallback _CreateCallBack;
        public static IAsyncResult _FindResult;
        public static AsyncCallback _FindCallBack;
        public static IAsyncResult _JoinResult;
        public static AsyncCallback _JoinCallBack;

        private static int _MaxLocal;
        private static int _MaxTotal;
        private static int _PrivateSlots;
        private static NetworkSessionProperties _NetProperties;
        private static int _SendCounter;
        private static int _SkipFrames;

    }
}
